﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.Linq;
using JetBrains.Annotations;
using Kaleida.ServiceMonitor.Model.Statistics;
using log4net;

namespace Kaleida.ServiceMonitor.UI
{
    public partial class OperationStatisticsControl : UserControl
    {
        private bool updatingUI;
        private const string NullIndicator = "-";
        private readonly ProcessingStatistics statistics;

        public OperationStatisticsControl(ProcessingStatistics statistics)
        {
            this.statistics = statistics;
            InitializeComponent();
            UpdateControlsBasedOnLogarithmicAxisSetting();
            UpdateUI();
        }

        [NotNull]
        public ProcessingStatistics Statistics
        {
            get {return statistics;}
        }

        public void UpdateUI()
        {
            if (updatingUI)
                return;

            lock (this)
            {
                updatingUI = true;
                SuspendLayout();

                lblMiniSummary.Text = BuildMiniSummary();

                tbDescription.Text = statistics.Description;

                tbRequestCount.Text = statistics.ProcessedCount.ToString("0");
                tbSuccessCount.Text = statistics.SuccessCount.ToString("0");
                tbFailureCount.Text = statistics.FailureCount.ToString("0");
                tbSuccessRate.Text = GetSuccessRateString();

                tbMeanRequestMs.Text = GetMillisecondText(statistics.MeanProcessingTime);
                tbMinRequestMs.Text = GetMillisecondText(statistics.MinimumProcessingTime);
                tbMaxRequestMs.Text = GetMillisecondText(statistics.MaximumProcessingTime);
                tbRecentRequestMs.Text = GetMillisecondText(statistics.MostRecentProcessingTime);

                var percentiles = statistics.GetResponseTimePercentiles(new[] {90m, 99m, 99.9m, 99.99m});
                tb1Nine.Text = GetMillisecondText(percentiles[0].Value);
                tb2Nines.Text = GetMillisecondText(percentiles[1].Value);
                tb3Nines.Text = GetMillisecondText(percentiles[2].Value);
                tb4Nines.Text = GetMillisecondText(percentiles[3].Value);

                var points = statistics.GetResponseTimeDistribution();
                UpdateChartUI(points);

                ResumeLayout();
                updatingUI = false;
            }
        }

        private string BuildMiniSummary()
        {
            return string.Format("{0:0} Request(s), {1:0} successful, {2:0} failed", statistics.ProcessedCount, statistics.SuccessCount, statistics.FailureCount);
        }

        private string GetSuccessRateString()
        {
            return statistics.ProcessedCount == 0
                ? NullIndicator
                : (((double)statistics.SuccessCount)/statistics.ProcessedCount).ToString("0.000%");
        }

        private static string GetMillisecondText(TimeSpan? value)
        {
            return value == null ? NullIndicator : value.Value.TotalMilliseconds.ToString("0");
        }

        private void UpdateChartUI(IList<DistributionPoint> points)
        {
            chtDistribution.Series.Clear();

            var series = new Series
                             {
                                 IsXValueIndexed = false,
                                 ChartType = SeriesChartType.Column,
                                 MarkerSize = 3,
                                 MarkerStyle = MarkerStyle.None,
                             };

            foreach (var point in points)
            {
                var dataPoint = new DataPoint(point.TimeMs, point.Count);
                series.Points.Add(dataPoint);
            }

            chtDistribution.Series.Add(series);

            var chartArea = chtDistribution.ChartAreas[0];
            chartArea.AxisX.IsLogarithmic = chkLogXAxis.Checked && points.Any();
            chartArea.AxisX.LogarithmBase = (double) nudLogarithmBase.Value;

            chartArea.RecalculateAxesScale();
        }

        private void chkLogXAxis_CheckedChanged(object sender, EventArgs e)
        {
            UpdateControlsBasedOnLogarithmicAxisSetting();
        }

        private void UpdateControlsBasedOnLogarithmicAxisSetting()
        {
            nudLogarithmBase.Enabled = chkLogXAxis.Checked;
        }

        private void OperationStatisticsControl_Resize(object sender, EventArgs e)
        {
            bool isMini = Width < 475 || Height < 200;

            chtDistribution.Visible = Height > 50 && Width > 50;
            chtDistribution.Dock = DockStyle.None; // cannot use Dock.Fill - it causes ArgumentException: Height must be greater than 0px.
            chtDistribution.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right;

            chtDistribution.Location = isMini ? new Point(0, 0) : new Point(3, 49);

            //Ensure width and height are never zero
            chtDistribution.Size = new Size(Math.Max(1, isMini ? Width : Width - 284), Math.Max(1, isMini ? Height : Height - 48)) ;

            tbDescription.Visible = !isMini;
            pnlAggregates.Visible = !isMini;
            gbXAxis.Visible = !isMini;
            lblMiniSummary.Visible = isMini;
            lblMiniSummary.Location = new Point(70, 0);
            lblMiniSummary.Size = new Size(Width - 70, 18);
        }
    }
}
